'''
Created on Aug 6, 2012

@author: levilucio
'''

import general_exceptions
import abstract_syntax_exceptions
import graphs

class Metamodel(graphs.TG):
    """ Class Metamodel """
    
    def __init__(self, V, E):
        """ build a metamodel which is a typed graph with a bijective mapping with the class type set """
        super(Metamodel, self).__init__(V, E)
        if not(self.__check_bijection()):
            raise abstract_syntax_exceptions.NotBijectiveMetamodelError()
    
    def __check_bijection(self):
        """ checks if there are no repeated types in the metamodel """
        values = self.V.values()
        for i in range(0, len(values)):
            for j in range(i + 1, len(values)):
                if (values[i] == values[j]): return False
        return True

    def printargs(self):
        """ Method docstring. """
        print "Metamodel"
        super(Metamodel, self).printargs()
    
    def export_dot(self, filehandle):
        for e in self.E:
            filehandle.write('   ' + self.V[e[0]] + ' -> ' + self.V[e[1]] + ';\n') 


class Model(graphs.TG):
    """ Class Model. A Model is a typed graph, typed by a Metamodel which is also a typed graph """
    
    def __init__(self, V, E, mm):
        """ build a metamodel which is a typed graph with a bijective mapping with the class type set """        
        super(Model, self).__init__(V, E)
        self.mm = mm
        if not(self.__check_conformance()):
            raise abstract_syntax_exceptions.ModelDoesNotConform()        

    def __check_conformance(self):
        """ check if a model is conforms to its metamodel """
        global_conf = True        
        for em in self.E:
            local_conf = False  
            for emm in self.mm.E:              
                if (self.V[em[0]] == self.mm.V[emm[0]]) and (self.V[em[1]] == self.mm.V[emm[1]]):
                    local_conf = True
            global_conf = global_conf and local_conf
        return global_conf
    
    def printargs(self):
        """ Method docstring. """      
        self.mm.printargs()
        print "Model"
        super(Model, self).printargs()

    def export_dot(self, filename):
        self.mm.export_dot(filename)
        super(Model, self).export_dot(filename)
        

class IndirectModel(graphs.ITG):
    """ Class IndirectModel. An Indirect Model is a indirect typed graph, typed by a Metamodel
        An Indirect Model is typed by a Metamodel but can have indirect links which transitively 
        span containment links"""
    
    def __init__(self, V, E, Il, mm):
        """ build a metamodel which is a typed graph with a bijective mapping with the class type set """
        super(IndirectModel, self).__init__(V, E, Il)          
        self.mm = mm
        if not(self.__check_conformance()):
            raise abstract_syntax_exceptions.ModelDoesNotConform()            

    def __check_conformance(self):
        """ check if a model is conforms to its metamodel """
        global_conf = True        
        for em in self.E:
            local_conf = False  
            for emm in self.mm.E:              
                if (self.V[em[0]] == self.mm.V[emm[0]]) and (self.V[em[1]] == self.mm.V[emm[1]]):
                    local_conf = True
            global_conf = global_conf and local_conf
        return global_conf
                         
    def printargs(self):
        """ Method docstring. """
        self.mm.printargs()
        print "IndirectModel"
        super(IndirectModel, self).printargs()

    def export_dot(self, filename):
        self.mm.export_dot(filename)
        super(IndirectModel, self).export_dot(filename)


class MatchApplyModel(graphs.TG):
    """ Match-Apply Model. A graph including both a match and an apply part
        that can be used to keep state during a transformation """
    
    def __init__(self, match, aply, bl):
        """ build a match apply model by using a match model, an apply model and a set of backward links """        
        if not isinstance(match, Model) : 
            raise general_exceptions.WrongParameter("Expecting an IndirectModel as first parameter of the constructor")
        if not isinstance(aply, Model) : 
            raise general_exceptions.WrongParameter("Expecting a Model as second parameter of the constructor")           
        self.match = match
        self.aply = aply
        if not self.__check_disjoint_vertices():
            raise abstract_syntax_exceptions.NotDisjointGraphs
        self.bl = bl
        if not(self.__check_bl_in_vertices()):
            raise abstract_syntax_exceptions.BackwardLinksNotSound
        match_V = match.V
        match_V.update(self.aply.V)
        super(MatchApplyModel, self).__init__(match_V, self.match.E.union(self.aply.E))

    def __check_bl_in_vertices(self):
        """ checks if all the backward links are directed edges from vertices of the match model to vertices of the apply model """
        for e in self.bl:
            if not (e[0] in set(self.match.V.keys()) and e[1] in set(self.aply.V.keys())): return False
        return True
    
    def __check_disjoint_vertices(self):
        if len(set(self.match.V.keys()).intersection(set(self.aply.V.keys()))) != 0 : return False
        return True

    def printargs(self):
        """ Method docstring. """
        print "MatchApplyModel"
        super(MatchApplyModel, self).printargs()
        print "MatchModel"
        self.match.printargs()
        print "ApplyModel"
        self.aply.printargs()   
        print "BackwardLinks"
        print self.bl

    def export_dot(self, filename):
        self.match.export_dot(filename)
        self.aply.export_dot(filename)

class TransformationRule(graphs.ITG):
    """ Transformation Rule. A transformation rule is a Match-Apply model
    with an indirect typed graph on the match part of the graph """ 

    def __init__(self, match, aply, bl):
        """ build a match apply model by using a match model, an apply model and a set of backward links """
        if not isinstance(match, IndirectModel) : 
            raise general_exceptions.WrongParameter("Expecting an IndirectModel as first parameter of the constructor")
        if not isinstance(aply, Model) : 
            raise general_exceptions.WrongParameter("Expecting a Model as second parameter of the constructor")        
        self.match = match
        self.aply = aply
        if not self.__check_disjoint_vertices():
            raise abstract_syntax_exceptions.NotDisjointGraphs
        self.bl = bl
        if not(self.__check_bl_in_vertices()):
            raise abstract_syntax_exceptions.BackwardLinksNotSound
        match_V = match.V
        match_V.update(self.aply.V)
        super(TransformationRule, self).__init__(match_V, self.match.E.union(self.aply.E), self.match.Il)

    def __check_bl_in_vertices(self):
        """ checks if all the backward links are directed edges from vertices of the match model to vertices of the apply model """
        for e in self.bl:
            if not (e[0] in set(self.match.V.keys()) and e[1] in set(self.aply.V.keys())): return False
        return True
    
    def __check_disjoint_vertices(self):
        if len(set(self.match.V.keys()).intersection(set(self.aply.V.keys()))) != 0 : return False
        return True

    def printargs(self):
        """ Method docstring. """
        print "TransformationRule"
        super(TransformationRule, self).printargs()
        print "MatchModel"
        self.match.printargs()
        print "ApplyModel"
        self.aply.printargs()   
        print "BackwardLinks"
        print self.bl

    def export_dot(self, filename):
        self.match.export_dot(filename)
        self.aply.export_dot(filename)
        

class Property(graphs.ITG):
    """ Transformation Rule. A transformation rule is a Match-Apply model
    with an indirect typed graph on the match part of the graph """ 

    def __init__(self, match, aply, bl):
        """ build a match apply model by using a match model, an apply model and a set of backward links """
        if not isinstance(match, IndirectModel) : 
            raise general_exceptions.WrongParameter("Expecting an IndirectModel as first parameter of the constructor")
        if not isinstance(aply, IndirectModel) : 
            raise general_exceptions.WrongParameter("Expecting an IndirectModel as second parameter of the constructor")        
        self.match = match
        self.aply = aply
        if not self.__check_disjoint_vertices():
            raise abstract_syntax_exceptions.NotDisjointGraphs
        self.bl = bl
        if not(self.__check_bl_in_vertices()):
            raise abstract_syntax_exceptions.BackwardLinksNotSound
        match_V = match.V
        match_V.update(self.aply.V)
        super(Property, self).__init__(match_V, self.match.E.union(self.aply.E), self.match.Il.union(self.aply.Il))

    def __check_bl_in_vertices(self):
        """ checks if all the backward links are directed edges from vertices of the match model to vertices of the apply model """
        for e in self.bl:
            if not (e[0] in set(self.match.V.keys()) and e[1] in set(self.aply.V.keys())): return False
        return True
    
    def __check_disjoint_vertices(self):
        if len(set(self.match.V.keys()).intersection(set(self.aply.V.keys()))) != 0 : return False
        return True

    def printargs(self):
        """ Method docstring. """
        print "Property"
        super(Property, self).printargs()
        print "MatchModel"
        self.match.printargs()
        print "ApplyModel"
        self.aply.printargs()   
        print "BackwardLinks"
        print self.bl
        
    def export_dot(self, filename):
        print "call Property export dot"
        self.match.export_dot(filename)
        self.aply.export_dot(filename)        

#v1 = {'a' : 'A', 'b' : 'B', 'c' : 'A', 'd' : 'D', 'e':'B'}
#
#e1 = [('a', 'b', 'containment'),
#      ('c', 'd', 'reference'),
#      ('b', 'c', 'reference'),
#      ('b', 'e', 'reference')]
#
#il1 = [('c', 'd', 'reference')]
#
##v2 = {'e' : 'A', 'f' : 'B', 'g' : 'A', 'h' : 'D'}
##
##e2 = [('e', 'f', 'containment'),
##      ('g', 'h', 'reference')]
##
###
#tg1 = ITG(v1, e1, il1)

#tg1.export_dot('/Users/levilucio/Desktop/tg1.dot')
#tg1.printargs()
#
#tg2 = TG(v2, e2)
#tg2.printargs()
#
##tg2 = TG(l2)
#
#tg1.union(tg2)
#tg1.printargs()
##
##il1 = [({'a' : 'A', 'b' : 'B'} , 'containment')]
##il2 = []
##
##tgi1 = ITG(tg1, il1)
##tgi2 = ITG(tg2, il2)
##
##tgi1.union(tgi2)
##tgi1.printargs()
##try:
##    mm = Metamodel(l1)
##except myExceptions.NotBijectiveMetamodelError:
##    print "Not a metamodel"
##mm.printargs()
#
##m = Model(l1, mm)
##m.printargs()

#v1 = {'a' : 'A', 'b' : 'B', 'c' : 'A', 'd' : 'B'}
#e1 = [('a', 'b', 'containment'),
#      ('c', 'd', 'reference')]
#
#v3 = {'a' : 'C', 'b' : 'D'}
#e3 = [('a', 'b', 'containment')]
#
#v2 = {'a' : 'A', 'b' : 'B', 'c' : 'C', 'd' : 'D'}
#e2 = [('a', 'b', 'containment'),
#      ('c', 'd', 'reference')]
#
#mm = Metamodel(v2, e2)
#
#m1 = Model(v1, e1, mm)
#m2 = Model(v2, e2, mm)

#bl = []
#
#ma = MatchApplyModel(m1, m2, bl)


#m.export_dot('/Users/levilucio/Desktop/tg1.dot')



